package strategies

import (
	"fmt"
	"github.com/nntaoli-project/goex"
	"github.com/patrickmn/go-cache"
	"log"
	"math"
	"quantization_trading/src/models"
	"quantization_trading/src/mycache"
	"quantization_trading/src/server"
	"quantization_trading/src/server/ws"
	"quantization_trading/src/util"
	"sync"
)
type kline struct {
	high float64
	low float64
	close float64
}

type StrategyWildFluctuation struct {
	Strategy
	OpenWave	float64	//开仓波动率
	CloseWave	float64 //平仓波动率
	StopLossRate	float64	//止损率
	Amount	float64	//首次购买数量
	Interval	int64	//自定义k线间隔(秒)
	HoldOrder models.Order	//持有订单
	IntentOrder models.Order //委托订单

	close                  chan bool
	lck      *sync.Mutex
	PriceMap map[int64] *kline
	newTime  int64
	MyKline  *kline
	StrategyInfoList map[int]*models.StrategyInfo

	OrderServer server.OrderServer


}

func NewWildFluctuation(strategyConfig models.Strategy) (*StrategyWildFluctuation,int) {
	baseStrategy := Strategy{
		StrategyName: strategyConfig.StrategyName,
		AccountName: strategyConfig.AccountName,
		Exchange: strategyConfig.Exchange,
		ContractType: strategyConfig.ContractType,
		Symbol: strategyConfig.Symbol,
	}
	swf := StrategyWildFluctuation{
		Strategy:baseStrategy,
		OpenWave: strategyConfig.OpenWave,
		CloseWave: strategyConfig.CloseWave,
		StopLossRate: strategyConfig.StopLoss,
		Amount: strategyConfig.Amount,
		Interval: strategyConfig.Interval,
		lck : new(sync.Mutex),
		PriceMap : make(map[int64] *kline),
		MyKline: &kline{
			high: -math.MaxFloat64,
			low: math.MaxFloat64,
		},
		StrategyInfoList: make(map[int]*models.StrategyInfo),
	}
	strategyKey := fmt.Sprintf("%s_%s",strategyConfig.StrategyName,strategyConfig.AccountName)
	if x,ok := mycache.OrderServerCache.Get(strategyConfig.AccountName);ok{
		swf.OrderServer = x.(server.OrderServer)
	}else {
		return nil,util.CREATE_STRATEGY_ERROR_NOT_ORDERSUB
	}

	mycache.StrategyCache.Set(strategyKey,&swf,cache.NoExpiration)
	code := models.AddStrategy(&strategyConfig)
	return &swf,code
}

func StartStrategyWildFluctuation(strategyName,accountName string) int{
	var sub ws.SubscribePriceStruct
	strategyKey := fmt.Sprintf("%s_%s",strategyName,accountName)
	var this *StrategyWildFluctuation
	if x,ok := mycache.StrategyCache.Get(strategyKey) ;ok{
		this = x.(*StrategyWildFluctuation)
		if !this.Status {
			priceKey := fmt.Sprintf("%s_%s_%s_price", this.Exchange, this.ContractType, this.Symbol)
			if x,ok := mycache.PriceCache.Get(priceKey); ok {
				sub = x.(ws.SubscribePriceStruct)
				//添加价格订阅(重要)
				sub.AddStrategy( strategyKey,func(trade * goex.Trade){
					//1.初始化临时变量
					temp_max := -math.MaxFloat64
					temp_min := math.MaxFloat64
					//2.新trade添加到priceMap
					this.lck.Lock()
					this.newTime = trade.Date /1000
					if key, fund := this.PriceMap[this.newTime]; fund {
						var k kline
						if trade.Price < key.low {
							k.low = trade.Price
						}else {
							k.low = key.low
						}
						if trade.Price > key.high {
							k.high = trade.Price
						}else {
							k.high = key.high
						}
						k.close = trade.Price
						this.PriceMap[this.newTime] = &k
					}else {
						this.PriceMap[this.newTime] = &kline{
							high:  trade.Price,
							low:  trade.Price,
							close:  trade.Price,
						}
					}
					//3。遍历priceMap删除不在范围时间，重新获取当前priceMap最大值和最小值
					for k,v := range this.PriceMap{
						if k >= this.newTime - this.Interval{
							if v.low  < temp_min {
								temp_min = v.low
							}
							if v.high > temp_max {
								temp_max = v.high
							}
						}else {
							delete(this.PriceMap,k)
						}
					}
					// 当前的最大值、最小值、最新值
					this.MyKline.high = temp_max
					this.MyKline.low = temp_min
					this.MyKline.close = trade.Price
					this.WaveOrder()
					this.lck.Unlock()
				})
				//添加订单订阅（重要）
				this.OrderServer.AddSubOrderServer(strategyKey, func(order models.Order) {
					//1.判断订单类型
					for _,strategyInfo := range this.StrategyInfoList{
						if order.Cid == strategyInfo.OpenCId{
							if strategyInfo.OpenExecutedQty != order.ExecutedQty{
								if order.Status == util.OVER_DONE && strategyInfo.CloseCId == ""{
									this.SendLimitOrder(strategyInfo,util.CLOSE)
								}
								strategyInfo.OpenExecutedQty = order.ExecutedQty
								models.UpdateStrategyInfo(strategyInfo)
							}
						}else if order.Cid == strategyInfo.CloseCId {
							if strategyInfo.CloseExecutedQty != order.ExecutedQty {
								strategyInfo.CloseExecutedQty = order.ExecutedQty
								if order.Status == util.OVER_DONE{
									strategyInfo.NewCycle += 1
									if strategyInfo.NewCycle == strategyInfo.AllCycle{
										strategyInfo.StatusUse = false
									}else {
										this.SendLimitOrder(strategyInfo,util.OPEN)
									}
									// 设置开仓订单平仓
									openOrder := models.FindOrder(strategyInfo.OpenCId)
									openOrder.StatusUse = false
									this.OrderServer.SaveOrder(openOrder)
									delete(this.StrategyInfoList, strategyInfo.Id)
								}
								models.UpdateStrategyInfo(strategyInfo)
							}
						}
						//else {
						//	StrategyLog.Error("没有找到对应等订单cid")
						//}
					}
				})
				strategy_info_list,_ :=models.FindByAccountNameAndStrategyStrategyInfo(strategyName,accountName)
				for _, strategy_info := range strategy_info_list{
					this.StrategyInfoList[strategy_info.Id] = &strategy_info
				}

				this.Status = true
				// 更新缓存
				mycache.StrategyCache.Set(strategyKey,this,cache.NoExpiration)
				// 更新数据库
				models.UpdateStrategy(strategyName,accountName,this.Status)
				return util.START_SUSSESS_STRATEGY
			}else {
				log.Println("没有订阅该数据")
				return util.NOT_SUB_PRICE
			}
		}else {
			return util.EIST_START_STRATEGY_ERROR
		}
	}
	return util.START_STRATEGY_ERROR_NOT_KEY
}

//处理价格
//func (this *StrategyWildFluctuation)HandlePrice(trade *goex.Trade)  {
//	//1.初始化临时变量
//	temp_max := -math.MaxFloat64
//	temp_min := math.MaxFloat64
//	//2.新trade添加到priceMap
//	this.lck.Lock()
//	this.newTime = trade.Date /1000
//	if key, fund := this.PriceMap[this.newTime]; fund {
//		var k kline
//		if trade.Price < key.low {
//			k.low = trade.Price
//		}else {
//			k.low = key.low
//		}
//		if trade.Price > key.high {
//			k.high = trade.Price
//		}else {
//			k.high = key.high
//		}
//		k.close = trade.Price
//		this.PriceMap[this.newTime] = &k
//	}else {
//		this.PriceMap[this.newTime] = &kline{
//			high:  trade.Price,
//			low:  trade.Price,
//			close:  trade.Price,
//		}
//	}
//	//3。遍历priceMap删除不在范围时间，重新获取当前priceMap最大值和最小值
//	for k,v := range this.PriceMap{
//		if k >= this.newTime - this.Interval{
//			if v.low  < temp_min {
//				temp_min = v.low
//			}
//			if v.high > temp_max {
//				temp_max = v.high
//			}
//		}else {
//			delete(this.PriceMap,k)
//		}
//	}
//	// 当前的最大值、最小值、最新值
//	this.MyKline.high = temp_max
//	this.MyKline.low = temp_min
//	this.MyKline.close = trade.Price
//	this.WaveOrder()
//	this.lck.Unlock()
//}


//Wave
func (this *StrategyWildFluctuation)WaveOrder(){
	if util.Volatility(this.MyKline.high , this.MyKline.close) > this.OpenWave{
		// 爆跌
		//发开仓单
		log.Println("爆跌")
		strategyInfo := this.GetStrategyInfo(util.BUY)
		// 开仓
		strategyInfo.OpenPrice = this.MyKline.close
		//平仓 精度写死（待改！！！）
		strategyInfo.ClosePrice = util.Decimal(this.MyKline.close *(1 + this.CloseWave))
		//止损
		strategyInfo.StopLoss = util.Decimal(this.MyKline.close *(1 - this.StopLossRate))
		models.UpdateStrategyInfo(strategyInfo)
		this.SendLimitOrder(strategyInfo, util.OPEN)
		this.initKline()
		return
	}else if util.Volatility(this.MyKline.close,this.MyKline.low) > this.OpenWave{
		// 爆涨
		log.Println("爆涨")
		//发开仓单
		strategyInfo := this.GetStrategyInfo(util.SELL)
		strategyInfo.OpenPrice = this.MyKline.close
		//精度写死（待改！！！）
		strategyInfo.ClosePrice = util.Decimal(this.MyKline.close *(1 - this.CloseWave))
		//止损
		strategyInfo.StopLoss = util.Decimal(this.MyKline.close *(1 + this.StopLossRate))
		models.UpdateStrategyInfo(strategyInfo)
		this.SendLimitOrder(strategyInfo, util.OPEN)
		//log.Printf("当前：%s,最高价：%f,最低价格:%f,最新价:%f",this.StrategyName,this.MyKline.high,this.MyKline.low,this.MyKline.close)
		this.initKline()
		return
	}
	for _, strategyInfo :=range this.StrategyInfoList{
		//判断是否有止损价格
		if strategyInfo.StopLoss != 0 {
			//判断方向
			if strategyInfo.Side == util.BUY && strategyInfo.StopLoss > this.MyKline.close{
				fmt.Println("触发止损")
				this.OrderServer.MarketOrderClose(strategyInfo.CloseCId)
				or := this.OrderServer.MarketOrderClose(strategyInfo.OpenCId)
				if or != nil{
					strategyInfo.CloseCId = or.Cid
				}
				strategyInfo.StatusUse = false
				models.UpdateStrategyInfo(strategyInfo)
				delete(this.StrategyInfoList, strategyInfo.Id)
			}else if strategyInfo.Side ==util.SELL && strategyInfo.StopLoss < this.MyKline.close{
				fmt.Println("触发止损")
				this.OrderServer.MarketOrderClose(strategyInfo.CloseCId)
				or := this.OrderServer.MarketOrderClose(strategyInfo.OpenCId)
				if or != nil{
					strategyInfo.CloseCId = or.Cid
				}
				strategyInfo.StatusUse = false
				models.UpdateStrategyInfo(strategyInfo)
				delete(this.StrategyInfoList, strategyInfo.Id)
			}
		}
	}
}



//初始化kline
func (this *StrategyWildFluctuation)initKline()  {
	this.MyKline = &kline{
		high: -math.MaxFloat64,
		low: math.MaxFloat64,
	}
	this.PriceMap = make(map[int64] *kline)
}

//获得StrategyInfo
func (this *StrategyWildFluctuation)GetStrategyInfo(side int) (*models.StrategyInfo) {
	for _,strategyInfo := range this.StrategyInfoList{
		if strategyInfo.Side == side{
			if strategyInfo.OpenCId == ""{
				return strategyInfo
			}else {
				// 如果有订单则撤销订单
				order := models.FindOrder(strategyInfo.OpenCId)
				if order != nil && order.ExecutedQty == 0{
					this.OrderServer.CancelOrder(order.Symbol,order.Cid)
					return strategyInfo
				}
			}
		}
	}
	return this.addStrategyInfo(side)
}

//添加StrategyInfo
func (this *StrategyWildFluctuation)addStrategyInfo(side int) *models.StrategyInfo {
	strategyInfo := &models.StrategyInfo{
		AccountName: this.AccountName,
		StrategyName: this.StrategyName,
		OpenAmount: this.Amount,
		CloseAmount: this.Amount,
		Side: side,
		AllCycle: 1,
		StatusUse: true,
	}
	strategyInfo, ok := models.AddStrategyInfo(strategyInfo)
	if ok{
		this.StrategyInfoList[strategyInfo.Id] = strategyInfo
		return strategyInfo
	}else {
		return nil
	}
}

//发送限价订单
func (this *StrategyWildFluctuation)SendLimitOrder(strategyInfo *models.StrategyInfo,OpenOrClose int) bool {
	switch OpenOrClose {
		case util.OPEN:
			switch strategyInfo.Side {
			case util.BUY:
				order := this.OrderServer.LimitOrder(this.Symbol,goex.FloatToString(strategyInfo.OpenPrice,10),goex.FloatToString(strategyInfo.OpenAmount,10),util.OPEN_BUY)
				if order != nil{
					strategyInfo.OpenCId = order.Cid
					models.UpdateStrategyInfo(strategyInfo)
					return true
				}else {
					return false
				}
			case util.SELL:
				order := this.OrderServer.LimitOrder(this.Symbol,goex.FloatToString(strategyInfo.OpenPrice,10),goex.FloatToString(strategyInfo.OpenAmount,10),util.OPEN_SELL)
				if order != nil{
					strategyInfo.OpenCId = order.Cid
					models.UpdateStrategyInfo(strategyInfo)
					return true
				}else {
					return false
				}
			}
		case util.CLOSE:
			switch strategyInfo.Side {
			case util.BUY:
				order := this.OrderServer.LimitOrder(this.Symbol,goex.FloatToString(strategyInfo.ClosePrice,10),goex.FloatToString(strategyInfo.CloseAmount,10),util.CLOSE_BUY)
				if order != nil{
					strategyInfo.CloseCId = order.Cid
					models.UpdateStrategyInfo(strategyInfo)
					return true
				}else {
					return false
				}
			case util.SELL:
				order := this.OrderServer.LimitOrder(this.Symbol,goex.FloatToString(strategyInfo.ClosePrice,10),goex.FloatToString(strategyInfo.CloseAmount,10),util.CLOSE_SELL)
				if order != nil{
					strategyInfo.CloseCId = order.Cid
					models.UpdateStrategyInfo(strategyInfo)
					return true
				}else {
					return false
				}
			}
	}
	return false
}

//当订单成交的时候判断，是否需要发平仓单等。。。。
//func (this *StrategyWildFluctuation)Handle(order models.Order)  {
//	//1.判断订单类型
//	for _,strategyInfo := range this.StrategyInfoList{
//		if order.Cid == strategyInfo.OpenCId{
//			if order.Status == util.OVER_DONE{
//				this.SendLimitOrder(strategyInfo,util.CLOSE)
//			}
//			strategyInfo.OpenExecutedQty = order.ExecutedQty
//		}else if order.Cid == strategyInfo.CloseCId {
//			if order.Status == util.OVER_DONE{
//				strategyInfo.NewCycle += 1
//				if strategyInfo.NewCycle == strategyInfo.AllCycle{
//					strategyInfo.StatusUse = false
//				}else {
//					this.SendLimitOrder(strategyInfo,util.OPEN)
//				}
//				models.UpdateStrategyInfo(strategyInfo)
//				// 设置开仓订单平仓
//				openOrder := models.FindOrder(strategyInfo.OpenCId)
//				openOrder.StatusUse = false
//				models.UpdateOrder(openOrder)
//				delete(this.StrategyInfoList, strategyInfo.Id)
//			}
//		}else {
//			StrategyLog.Error("没有找到对应等订单cid")
//		}
//	}
//}